﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages;
using System.Web.WebPages.Html;
using umbraco.MacroEngines;
using Eksponent.RazorRocks.Helpers;

namespace Components
{

    public class PageableConfig
    {
        public string Prefix { get; set; }
        public int[] PageSizes { get; set; }
        public int DefaultPageSize { get; set; }

        

        /// <summary>
        /// The number of pages on each side of the current page
        /// </summary>
        public int PagerWidth { get; set; }

        public PageableConfig()
        {
            Prefix = "";
            DefaultPageSize = 20;
            PagerWidth = 1;
        }
    }

    public class PageLinkModel
    {
        public bool Selected { get; set; }
        public string Text { get; set; }
        public string Url { get; set; }
    }

    public static class Pageable
    {
        private static readonly PageableConfig _defaultSettings = new PageableConfig();
        public static PageableConfig DefaultSettings { get { return _defaultSettings; } }


        public static HelperResult Write<TItem>(IPageable<TItem> items, Func<ViewModel<TItem>, HelperResult> template)
        {
            return Write(items, null, template);
        }

        public static HelperResult Write<TItem>(IPageable<TItem> items, PageableConfig config, Func<ViewModel<TItem>, HelperResult> template)
        {

            config = config ?? _defaultSettings;

            var pageParam = config.Prefix + "p";
            var pageSizeParam = config.Prefix + "pz";

            var model = new ViewModel<TItem>
                            {
                                CurrentPage = QueryStringHelper.Get<int>(pageParam),
                                PageSize = QueryStringHelper.Get(pageSizeParam, config.DefaultPageSize)
                            };

            if (config.PageSizes == null || !config.PageSizes.Contains(model.PageSize))
            {
                model.PageSize = config.DefaultPageSize;
            }

            var count = items.Count;
            model.PageCount = Math.Max(1, (int)Math.Ceiling(count / (double)model.PageSize));

            model.CurrentPage = Math.Min(Math.Max(1, model.CurrentPage), model.PageCount);

            if (model.CurrentPage < 1)
            {
                model.CurrentPage = 1;
            }
            else if (model.CurrentPage > model.PageCount)
            {
                model.CurrentPage = model.PageCount;
            }

            model.PrevUrl = model.CurrentPage > 1
                                ? QueryStringHelper.PatchUrl(pageParam, model.CurrentPage - 1)
                                : null;

            model.NextUrl = model.CurrentPage < model.PageCount
                                ? QueryStringHelper.PatchUrl(pageParam, model.CurrentPage + 1)
                                : null;

            model.Pages = new List<PageLinkModel>();
            foreach (var page in GetPagesToShow(config.PagerWidth, model.CurrentPage, model.PageCount))
            {
                model.Pages.Add(new PageLinkModel
                                    {
                                        Selected = page != null && page.Value == model.CurrentPage,
                                        Text = page != null ? "" + page.Value : "…",
                                        Url = page != null ? QueryStringHelper.PatchUrl(pageParam, page.Value) : null
                                    });
            }

            model.PageSizes = new List<Tuple<int, string>>();
            if (config.PageSizes != null && config.PageSizes.Length > 1)
            {
                model.PageSizes.AddRange(config.PageSizes.Select(sz => Tuple.Create(sz, QueryStringHelper.PatchUrl(pageSizeParam, sz))));
            }


            model.Items = items.GetPage((model.CurrentPage - 1)*model.PageSize, model.PageSize).ToList();

            return template(model);
        }

        private static IEnumerable<int?> GetPagesToShow(int width, int current, int count)
        {
            if (count < 2 * width + 1)
            {
                for (int i = 1; i <= count; i++)
                {
                    yield return i;
                }
            }
            else
            {
                yield return 1;

                int center = Math.Min(count - width, Math.Max(1 + width, current));

                if (1 < center - width - 1)
                {
                    yield return null;
                }


                for (int i = center - width; i <= center + width; i++)
                {
                    if (i > 1 && i < count)
                    {
                        yield return i;
                    }
                }

                if (center + width < count - 1)
                {
                    yield return null;
                }

                yield return count;
            }
        }

        public class ViewModel<TItem>
        {
            public string PrevUrl { get; set; }

            public string NextUrl { get; set; }

            public int CurrentPage { get; set; }

            public int PageCount { get; set; }

            public int PageSize { get; set; }

            public List<PageLinkModel> Pages { get; set; }

            public List<TItem> Items { get; set; }

            /// <summary>
            /// Size, Url
            /// </summary>
            public List<Tuple<int, string>> PageSizes { get; set; }            
        }
    }
}